import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.ListIterator;

public class Borg {
	public static float P_INF = 32768;
	public static float N_INF = -32768;
	public static final Edge TOP_EDGE = new Edge(N_INF, N_INF, P_INF * 2f, Edge.HORIZONTAL, Edge.LOW);
	public static final Edge BTM_EDGE = new Edge(N_INF, P_INF, P_INF * 2f, Edge.HORIZONTAL, Edge.HIGH);
	public static final Edge LEFT_EDGE = new Edge(N_INF, N_INF, P_INF * 2f, Edge.VERTICAL, Edge.LOW);
	public static final Edge RIGHT_EDGE = new Edge(P_INF, N_INF, P_INF * 2f, Edge.VERTICAL, Edge.HIGH);
	
	ArrayList rects;
	RectNode bounds;
	RectNode inf;
	
	
	
	public Borg(){
		rects = new ArrayList();
		bounds = new RectNode(0, 0, 0, 0);
		inf= new RectNode(N_INF, N_INF, P_INF * 2f, P_INF * 2f);
	}
	
	public void assimilate(RectNode rn){
		ArrayList edges = getEdges(-1);
		int i = 0;
		while(i < rn.edges.size()){
			Edge e1 = (Edge)rn.edges.get(i);
			boolean removed = false;
			int ii = 0;
			while(ii < edges.size() && !removed){
				Edge e2 = (Edge)edges.get(ii);
				int cons[] = assimilateOverlap(e1, e2);
				if(cons[0] >= DELETE){
					e1.clear();
					removed = true;
					i--;
				}
				if(cons[0] == SWAP){
					edges.add(e1);
					e2.parent.addEdge(e1);
				}
				if(cons[1] >= DELETE){
					e2.clear();
					edges.remove(ii);
					ii--;
				}
				if(cons[1] == SWAP)
					e1.parent.addEdge(e2);
				ii++;
			}
			i++;
		}
		clearAnchors();
		rects.add(rn);
		bounds = (RectNode) bounds.newBoundingBox(rn);
	}
	
	
	
	public static final int KEEP = 0;
	public static final int DELETE = 1;
	public static final int SWAP = 2;
	private int[] assimilateOverlap(Edge e1, Edge e2){
		int ret[] = new int[]{0, 0};	
		if(e1.equalPerp(e2)){
			int overlap = e1.overlap(e2);
			float es1 = e1.getParVal();
			float es2 = e2.getParVal();
			float ee1 = es1 + e1.l;
			float ee2 = es2 + e2.l;
			switch(overlap){
			case Edge.TOUCH_BEG:
				e1.pos = e2.pos;
				e1.l += e2.l;
				e1.mergeAnchors(e2);
				ret[1] = DELETE;
				break;
			case Edge.TOUCH_END:
				e1.l += e2.l;
				e1.mergeAnchors(e2);
				ret[1] = DELETE;
				break;
			case Edge.OVERLAP_BEG:
				e2.l = es1 - es2;
				if(e1.l == 0) ret[0] = DELETE;
				e1.setParVal(ee2);
				e1.l = ee1 - ee2;
				if(e2.l == 0) ret[1] = DELETE;
				break;
			case Edge.OVERLAP_END:
				e1.l = es2 - es1;
				if(e1.l == 0) ret[0] = DELETE;
				e2.setParVal(ee1);
				e2.l = ee2 - ee1;
				if(e2.l == 0) ret[1] = DELETE;
				break;
			case Edge.ENCOMPASSED:
				
				e2.l = es1 - es2;
				e2.parent.addEdge(e1);
				if(e2.l == 0) ret[1] = DELETE;
				
				e1.setParVal(ee1);
				e1.l = ee2 - ee1;
				e1.hilo = e2.hilo;
				if(e1.l == 0) ret[0] = DELETE;
				else ret[0] = SWAP;
				break;
			case Edge.ENCOMPASSES:
				e1.l = es2 - es1;
				e1.parent.addEdge(e2);
				if(e1.l == 0) ret[1] = DELETE;
				e2.setParVal(ee2);
				e2.l = ee1 - ee2;
				e2.hilo = e1.hilo;
				if(e2.l == 0) ret[1] = DELETE;
				else ret[1] = SWAP;
				break;
			case Edge.EQUAL:
				ret[0] = DELETE;
				ret[1] = DELETE;
			}
		}
		return ret;
	}
	
	public ArrayList getIntersects(Edge e, int minType){
		ArrayList edges = getEdges();
		edges.addAll(inf.getEdges());
		//System.out.println(listToString(edges));
		Iterator ei = edges.iterator();
		ArrayList x = new ArrayList();
		while(ei.hasNext()){
			Intersect is = new Intersect(e, (Edge)ei.next());
			//System.out.println(is.toString());
			if(is.type >= minType)
				x.add(is);
		}
		Collections.sort(x, new Intersect.Comp());
		//System.out.println(listToString(x));
		removeDuplicates(x);
		return x;
	}
	
	private void removeDuplicates(ArrayList x){
		ListIterator i = x.listIterator();
		Intersect prev = null;
		while(i.hasNext()){
			Intersect cur = (Intersect)i.next();
			if(prev != null){
				if(cur.e2.getPerpVal() == prev.e2.getPerpVal()){
					if(cur.type > prev.type){
						i.previous();
						i.previous();
						i.remove();
						i.next();
					}
					else i.remove();
				}
			}
			prev = cur;
		}
	}
	
	public float getClosestIntersect(Edge e, ArrayList x){
		Iterator i = x.iterator();
		float dist = P_INF;
		while(i.hasNext()){
			Intersect cur = (Intersect)i.next();
			float d;
			if(e.hilo == Edge.HIGH){
				d = cur.e2.getParDistance(e);
			}
			else{
				d = e.getParDistance(cur.e2);
			}
			if(d < dist && d > 0){
				dist = d;
			}
		}
		return dist;
	}
	
	protected ArrayList filterIntersects(Edge e, ArrayList x){
		boolean blocked = true;
		boolean hasContact = false;
		boolean blockBefore = false;
		boolean blockAfter = false;
		ArrayList filtered = new ArrayList();
		Iterator i = x.iterator();
		while(i.hasNext()){
			Intersect ix = (Intersect)i.next();
			if(ix.type == Intersect.BLOCKED){
				if(blocked){
					filtered.add(ix);
					hasContact = ix.onEdge() == Intersect.ON_EDGE;
				}
				else if(!hasContact){
					
					filtered = new ArrayList();
				}
				else if(hasContact){
					filtered.add(ix);
					while(i.hasNext()) i.next();
				}
				blocked = !blocked;
				if(hasContact) blockBefore = true;
				else blockAfter = true;
			}
			else if(!blocked){
				filtered.add(ix);
				hasContact = ix.onEdge() == Intersect.ON_EDGE;
			}
			
		}
		return filtered;
	}
	
	public ArrayList getEdges(){
		return getEdges(-1);
	}
	public ArrayList getEdges(int s){
		ArrayList ret = new ArrayList();
		Iterator i = rects.iterator();
		while(i.hasNext()){
			ret.addAll(((RectNode)i.next()).getEdges(s));
		}
		return ret;
	}
	
	public void clearAnchors(){
		Iterator i = getEdges().iterator();
		while(i.hasNext()){
			((Edge)i.next()).clearAnchors();
		}
	}
	
	public ArrayList getEdges(RectNode b){
		ArrayList ret = new ArrayList();
		Iterator i = rects.iterator();
		while(i.hasNext()){
			Iterator e = ((RectNode)i.next()).getEdges(-1).iterator();
			while(e.hasNext()){
				Edge edge = (Edge)e.next();
				if(edge.isInside(b) > Edge.NOT_INSIDE)
					ret.add(edge);
			}
		}
		return ret;
	}
	
	public int numRects(){
		return rects.size();
	}
	


	public String listToString(ArrayList l){
		StringBuffer sb = new StringBuffer();
		sb.append("LIST:::");
		sb.append(l.size());
		Iterator i = l.iterator();
		int p = 0;
		while(i.hasNext()){
			sb.append("\n" + p + "  ");
			p++;
			Object o = i.next();
			if(o instanceof Edge)
				sb.append(((Edge)o).toString());
			else if(o instanceof Space)
				sb.append(((Space)o).toString());
			else if(o instanceof Intersect)
				sb.append(((Intersect)o).toString());
			else if(o instanceof Anchor)
				sb.append(((Anchor)o).toString());
		}
		return sb.toString();
	}
	
	public boolean intersects(Rectangle r){
		Iterator e = getEdges().iterator();
		Edge top = new Edge(r, Edge.TOP);
		Edge btm = new Edge(r, Edge.BTM);
		Edge left = new Edge(r, Edge.LEFT);
		Edge right = new Edge(r, Edge.RIGHT);
		while(e.hasNext()){
			Edge edge = (Edge)e.next();
			if(edge.intersects(top)) return true;
			if(edge.intersects(btm)) return true;
			if(edge.intersects(left)) return true;
			if(edge.intersects(right)) return true;
		}
		return false;
	}

}